function mkDropDown(input, id) {
	// create dropdown immediately below input field.
	var o = document.createElement("select");

    // link dropdown and input
	o.input = input; 
	input.dropdown = o;

    // set display properties
	o.id = id;
    o.style.zIndex = 10; // put menu in front of other things
	o.style.textOverflow = "ellipsis";

    // whether dropdown will mark text simulating auto-completion
	o.auto_complete = input.setSelectionRange != undefined;

    // insert just after input field as hidden.
	o.style.display = "none";
	input.parentNode.appendChild(o);

	o.populate = function(choices) {
		// remember previous value to allow resetting it
		var oldval = this.value;

    // we don't present user with menu if there are no choices,
    // or if single choice can be handled with auto-completion in 
    // input field.
    var any_choice = choices.length > 0;
    if(choices.length == 1 && this.auto_complete) {
        var inputval = this.unselectedValue().toLowerCase();
        var optval = choices[0].toLowerCase();
        var is_prefix = optval.indexOf(inputval) == 0;
        any_choice = !is_prefix;
    }
    if(!any_choice) this.style.display = "none";

    // scroll beyond 10th entry
    if(choices.length > 5) {
      this.size = 5;
      this.style.overflow = "scroll";
    } else {
      this.size = choices.length;
      this.style.overflow = "hidden";
    }
    
    // we ensure dropdown is shown in the same way, regardless of the number of choices.
    // It doesn't matter we allow multiple selections, when there aren't multiple 
    // choices to make.
    this.multiple = choices.length <= 1;

		// remove previous entries
		while(this.firstChild != null) this.removeChild(this.firstChild);

		// we use index of previously selected value or select
		// first one (if any) otherwise.
        var useidx = choices.length == 0 ? -1 : 0;
		for(var k = 0; k < choices.length; ++k) {
			if(choices[k] == oldval) oldidx = k;
			this.options[this.options.length] = new Option(choices[k],
														   choices[k]);
		}
    if(useidx >= 0) this.setChoice(useidx);

		if(any_choice) this.style.display = "block";		
	};

    // get unselected part of value
    o.unselectedValue = function() {
		if(this.input.selectionEnd == undefined) return this.input.value;

		var end = this.input.selectionEnd, val = this.input.value;
		return (end != val.length) ? val : 
		       val.substring(0, this.input.selectionStart);
	};

	// change input-field on selection
    o.onchange = function() {
        if(this.on_change) this.on_change(this.selectedIndex);

        var oldval = this.unselectedValue();
        var idx = -1;
	    if(this.value && oldval) idx = this.value.toLowerCase().indexOf(oldval.toLowerCase());
		
        if(this.auto_complete) {
            if(idx == 0) {
                this.input.value = this.value;
                this.input.setSelectionRange(idx + oldval.length, 
			  						         this.input.value.length);
            } else this.input.value = oldval;
		}
    
		this.input.focus();
	};

  o.hide = function() {
    this.style.display = "none";
  };

  o.choose = function() {
    var idx = this.selectedIndex;
    if(idx >= 0 && idx < this.length) {
      this.input.value = this.value;
      
      // select all and more pointer to end
      if(this.input.on_choose) this.input.on_choose(this.value);
      this.input.select();
    }
    this.hide();
  };

  o.setChoice = function(idx) {
	  this.selectedIndex = idx;
		this.onchange();
  }

  o.moveChoice = function(jump) {			
		var idx = this.selectedIndex;
		if(this.length == 0) idx = -1;
		else {
			idx += jump;
			if(idx < 0) idx = 0;
			if(idx >= this.length) idx = this.length - 1;
		}
		this.setChoice(idx);
	};

	// capture up-down arrows from input field
	input.previous_onkeydown = input.onkeydown;
    input.onkeydown = function(evt) {
        var e = window.event ? event : evt;
        var key = e.keyCode ? e.keyCode : e.charCode;

        var target = e.target ? e.target : e.srcElement;
        if(!target) inform("no target");
        var dropdown = target.dropdown;
    
        // return or tab selects active choice, and is acted upon 
		// whether menu is active or not other keys demand visible menu.
        if((key == 13 || key == 9) && dropdown.selectedIndex >= 0) {
            dropdown.choose();
            return key == 9; // tab is sent along, return is eaten.
        }
    
        if(dropdown.style.display != "none") {
            if(key == 40 || (key == 78 && e.ctrlKey)) { // arrow down or ^N
                dropdown.moveChoice(1); 
                return false;
            }
            if(key == 38 || (key == 80 && e.ctrlKey)) { // arrow up or ^P
                dropdown.moveChoice(-1); 
                return false;
            }
            if(key == 27) { // escape
              dropdown.input.value = dropdown.unselectedValue();
              dropdown.hide();
              return false;
            }
        }
        
        if(!target) return true;
        if(!target.previous_onkeydown) return true;
        return target.previous_onkeydown(evt);
	};
  
  o.ondblclick = function(evt) {
    if(this.selectedIndex >= 0) this.choose();
  };
  
  // check for clicks outside dropdown, to hide it.
  document.onclick= function(e) {
    var target = (e && e.target) || (event && event.srcElement); 
    var dropdown = document.getElementById(id);
    var input = dropdown.input;
    if(target != dropdown && target != input) dropdown.hide();
    return true;
  }; 

	return o;
}
